// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package obj

import "cmd/internal/src"

// InlTree是内联调用的集合。字段的父字段
// InlinedCall是InlTree中另一个InlinedCall的索引。
// None
// 编译器维护一个全局内联树并向其中添加一个节点
// 每次函数内联时。例如，假设f（）调用g（）
// g有两个对h（）的调用，并且f、g和h是可内联的：
// None
// 1 func main（）{
// 2 f（）
// 3 }
// 4 func f（）{
// 5克（）
// 6 }
// 7 func g（）{
// 8小时（）
// 9小时（）
// 10 }
// 11 func h（）{
// 12 println（“H”）
// 13 }
// None
// 假设全局树开始为空，内联将生成
// 以下树：
// None
// []内联调用{
// {Parent:-1，Func:“f”，Pos:<line 2>}，
// {父项：0，函数：“g”，位置：<line 5>，
// {父：1，Func:“h”，位置：<line 8>，
// {父：1，Func:“h”，位置：<line 9>，
// }
// None
// 内联到main中的h节点将具有内联索引2和3。
// None
// 最后，编译器从中提取每个函数的内联树
// 全局内联树（参见pcln.go）。
type InlTree struct {
	nodes []InlinedCall
}

// InlinedCall是InlTree中的一个节点。
type InlinedCall struct {
	Parent   int      // InlTree中父级的索引，如果最外层调用，则索引<0
	Pos      src.XPos // 内联调用的位置
	Func     *LSym    // 内联的函数
	ParentPC int32    // 内联体前的PC指令。仅在本地树中有效。
}

// Add向树添加一个新调用，返回其索引。
func (tree *InlTree) Add(parent int, pos src.XPos, func_ *LSym) int {
	r := len(tree.nodes)
	call := InlinedCall{
		Parent: parent,
		Pos:    pos,
		Func:   func_,
	}
	tree.nodes = append(tree.nodes, call)
	return r
}

func (tree *InlTree) Parent(inlIndex int) int {
	return tree.nodes[inlIndex].Parent
}

func (tree *InlTree) InlinedFunction(inlIndex int) *LSym {
	return tree.nodes[inlIndex].Func
}

func (tree *InlTree) CallPos(inlIndex int) src.XPos {
	return tree.nodes[inlIndex].Pos
}

func (tree *InlTree) setParentPC(inlIndex int, pc int32) {
	tree.nodes[inlIndex].ParentPC = pc
}

// OutermostPos返回与XPO对应的最外层位置，
// 这就是XPO最终内联到的地方。以
// InlTree，main（）包含来自h（）的内联AST节点，但
// 这些节点的最外层位置是第2行。
func (ctxt *Link) OutermostPos(xpos src.XPos) src.Pos {
	pos := ctxt.InnermostPos(xpos)

	outerxpos := xpos
	for ix := pos.Base().InliningIndex(); ix >= 0; {
		call := ctxt.InlTree.nodes[ix]
		ix = call.Parent
		outerxpos = call.Pos
	}
	return ctxt.PosTable.Pos(outerxpos)
}

// InnermostPos返回与xpos对应的最内层位置，
// 也就是说，代码是内联的，而不是内联的。
// 在上面的InlTree示例中，h中println的代码
// 将有一个最里面的位置，行号为12，是否
// h不是内联的，内联到g、g-then-f或g-then-f-then-main中。
// 这对应于调试main、f、g或h时可能出现的情况
// 希望看到，而单步。
func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos {
	return ctxt.PosTable.Pos(xpos)
}

// AllPos返回xpos内联位置的一部分，从
// 从最内层（索引零）到最外层。避免无偿分配
// 如果需要，将传入并扩展结果。
func (ctxt *Link) AllPos(xpos src.XPos, result []src.Pos) []src.Pos {
	pos := ctxt.InnermostPos(xpos)
	result = result[:0]
	result = append(result, ctxt.PosTable.Pos(xpos))
	for ix := pos.Base().InliningIndex(); ix >= 0; {
		call := ctxt.InlTree.nodes[ix]
		ix = call.Parent
		result = append(result, ctxt.PosTable.Pos(call.Pos))
	}
	return result
}

func dumpInlTree(ctxt *Link, tree InlTree) {
	for i, call := range tree.nodes {
		pos := ctxt.PosTable.Pos(call.Pos)
		ctxt.Logf("%0d | %0d | %s (%s) pc=%d\n", i, call.Parent, call.Func, pos, call.ParentPC)
	}
}
